用 Python 解析 HTML 的三招五式

您所在的位置:网站首页 html < 编码 用 Python 解析 HTML 的三招五式

用 Python 解析 HTML 的三招五式

2024-01-17 13:13| 来源: 网络整理| 查看: 265

第一招 LXML

本节主要介绍 XPath 和解析库 LXML 的使用。

XPath & LXML

XPath (XML Path Language) 是设计来在XML文档中查找信息的语言,它同样适用于HTML。

我们在爬虫时,可以使用 XPath 来做相应的信息抽取。

⚠️【注意】需要安装好 LXML。

XPath常用规则 表达式描述nodename选取此节点的所有子节点/从当前节点选取直接子节点//从当前节点选取子孙节点.选取当前节点..选取当前节点的父节点@选取属性

我们常用 // 开头的 XPath 规则来选取所有符合要求的节点。

另外,常用运算符见 XPath 运算符。

导入 HTML 从字符串导入 HTML

导入了 LXML 库的 etree 模块,然后声明了一段 HTML 文本,调用 HTML 类进行初始化,这样我们就成功构造了一个 XPath 解析对象。

⚠️【注意】etree 模块可以对 HTML 文本进行修正。

调用 tostring() 方法即可输出修正后的 HTML 代码,结果是 bytes 类型(可以利用 decode() 方法转成 str 类型)

from lxml import etree text = ''' first item second item third item fourth item fifth item ''' html = etree.HTML(text) result = etree.tostring(html) print(result.decode('utf-8')) 从文件导入 HTML from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = etree.tostring(html) print(result.decode('utf-8')) 获取节点 获取所有节点

获取一个 HTML 中的所有节点,使用规则 //*:

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//*') print(result)

我们得到了一个 由 Element 类型组成的列表。

获取所有指定标签

如果我们想获取所有 li 标签,我们可以把上例中的html.xpath() 中的规则改为 '//li':

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li') print(result)

如果无法获取任何匹配结果,html.xpath 将会返回 []

获取子节点

选择 li 节点所有直接 a 子节点,使用规则 '//li/a':

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li/a') print(result)

要获取其下所有子孙a节点可以这样://li//a

获取特定属性的节点

用 @ 符号进行属性过滤。 smt[...] 是有 ... 限制的smt。

选中 href 是 link4.html 的 a 节点,规则是 '//a[@href="link4.html"]:

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//a[@href="link4.html"]') print(result) 获取父节点

如果我们想获取上例的父节点, 然后再获取其 class 属性:

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//a[@href="link4.html"]/../@class') # 也可以用“节点轴” '//a[@href="link4.html"]/parent::*/@class' print(result)

关于节点轴的使用,详见 XPath Axes

获取节点中的的文本

XPath 中的 text() 方法可以获取节点中的直接文本(不包括其子节点中的文本)。

from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li[@class="item-0"]/text()') print(result) 获取属性 from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li/a/@href') print(result)

用上面这种方法只能获取只有一个值的属性, 对于下面这种:

first item

li 节点的 class 属性有两个值,上面这个方法会失效,我们可以使用 contains() 函数:

from lxml import etree text = ''' first item ''' html = e#tree.HTML(text) result = html.xpath('//li[contains(@class, "li")]/a/text()') print(result)

这里还可以使用运算符 and 来连接:

from lxml import etree text = ''' first item ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()') print(result) 补充

点击链接查看详细的 XPath 教程 、 lxml 库。

第二招 BeautifulSoup

本节主要介绍解析库 BeautifulSoup 的使用。

BeautifulSoup

BeautifulSoup 提供一些简单的、Python式的函数用来处理导航、搜索、修改分析树等功能。它通过解析文档为用户提供需要抓取的数据。利用它我们可以提高解析效率。

BeautifulSoup 拥有完善的官方中文文档,可以查看 BeautifulSoup官方文档

⚠️【注意】需要安装好 BeautifulSoup 和 LXML。

BeautifulSoup 可以使用多种解析器,主要的几种如下:

解析器使用方法优势劣势Python标准库BeautifulSoup(markup, "html.parser")Python的内置标准库、执行速度适中 、文档容错能力强Python 2.7.3 or 3.2.2)前的版本中文容错能力差LXML HTML 解析器BeautifulSoup(markup, "lxml")速度快、文档容错能力强需要安装C语言库LXML XML 解析器BeautifulSoup(markup, "xml")速度快、唯一支持XML的解析器需要安装C语言库html5libBeautifulSoup(markup, "html5lib")最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档速度慢、不依赖外部扩展

我们一般使用 LXML 解析器来进行解析,使用方法如下:

from bs4 import BeautifulSoup soup = BeautifulSoup('

Hello

', 'lxml') print(soup.p.string) BeaufulSoup对象的初始化

使用如下代码就可以导入HTML,完成BeautifulSoup对象的初始化,并自动更正(如闭合未闭合的标签)。

soup = BeautifulSoup(markup, "lxml") # markup 是 HTML 的 str

初始化之后我们还可以对要解析的字符串以标准的缩进格式输出:

print(soup.prettify()) 节点选择器 选择标签

选择元素的时候直接通过调用节点的名称就可以选择节点元素, 调用 string 属性就可以得到节点内的文本。

from bs4 import BeautifulSoup html = """ The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; and their names were , Lacie and Tillie; and they lived at the bottom of a well.

...

""" soup = BeautifulSoup(html, 'lxml') print(soup.title) # The Dormouse's story print(type(soup.title)) # print(soup.title.string) # The Dormouse's story print(soup.head) # The Dormouse's story print(soup.p) #

The Dormouse's story

嵌套选择

我们还可以进行 嵌套选择,即做类似 父.子.孙 的选择:

print(soup.head.title.string) 关联选择

有时候我们难以做到一步就可以选择到想要的节点元素,这时我们可以先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等等

获取子孙节点

选取到了一个节点元素之后,如果想要获取它的直接 子节点 可以调用 contents 属性,将返回一个依次列有所有子节点的list。

如p标签之类的节点中可能既包含文本,又包含节点,返回的结果会将他们以列表形式都统一返回。

soup.p.contents # 注意里面的文字被切成了几部分 '''(result) [ 'Once upon a time ... were\n', , ',\n', Lacie, ' and\n', Tillie, ';\nand ... well.' ] '''

同时,查询 子节点,我们还可以使用 children 属性,它将返回一个 list_iterator object,化为 list 之后,就和 contents 一样了:

>>> s.p.children >>> a = list(soup.p.children) >>> b = soup.p.contents >>> a == b True

我们可以逐个编号输出子节点:

for i, child in enumerate(soup.p.children): print(i, child)

要得到所有的 子孙节点(所有下属节点)的话可以调用 descendants 属性,descendants 会递归地查询所有子节点(深度优先),得到的是所有的子孙节点,返回结果是一个 :

from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.p.descendants) for i, d in enumerate(soup.p.descendants): print(i, d) 获取父节点和祖先节点

如果要获取某个节点元素的父节点,可以调用 parent 属性,返回一个节点:

>>> soup.span.parent # 结果是

...

如果我们要想获取所有的祖先节点(一层层向上找,直到整个html),可以调用 parents 属性,返回一个generator:

>>> soup.span.parents >>> list(soup.span.parents) # 结果是 [

...

, ..., ..., ...]

⚠️【注意】父是 parent,祖先是 parents

获取兄弟节点

要获取同级的节点也就是兄弟节点,我们可以调用了四个不同的属性,它们的作用不尽相同:

next_sibling:获取节点向下一个兄弟节点,返回节点。 previous_sibling:获取向上一个兄弟节点,返回节点。 next_siblings:获取向下所有兄弟节点,返回一个generator。 previous_siblings:获取向上所有兄弟节点,返回一个generator。 >>> from bs4 import BeautifulSoup >>> html = """ ... ... ...

... Once upon a time there were three little sisters; and their names were ... ... Elsie ... ... Hello ... Lacie ... and ... Tillie ... and they lived at the bottom of a well. ...

... """ >>> soup = BeautifulSoup(html, 'lxml') >>> soup.a Elsie >>> soup.a.next_sibling '\n Hello\n ' >>> soup.a.previous_sibling '\n Once upon a time there were three little sisters; and their names were\n ' >>> soup.a.next_siblings >>> soup.a.previous_siblings >>> for i in soup.a.previous_siblings: ... print(i) ... Once upon a time there were three little sisters; and their names were >>> for i in soup.a.next_siblings: ... print(i) ... Hello Lacie and Tillie and they lived at the bottom of a well. >>> 方法选择器

有时难以利用节点选择器直接找到想要的节点时,我们可以利用 find_all()、find() 等方法,传入相应等参数就可以灵活地进行查询,得到想要的节点,然后通过关联选择就可以轻松获取需要的信息。

find()

find() 传入一些属性或文本来得到符合条件的元素,返回第一个匹配的元素。

find(name , attrs , recursive , text , **kwargs)

使用实例如下:

from bs4 import BeautifulSoup html=''' Hello Foo Bar Jay Foo Bar ''' soup = BeautifulSoup(html, 'lxml') print(soup.find(name='ul') print(soup.find(attrs={'class': 'element'})) print(soup.find(text=re.compile('.*?o.*?', re.S))) # 结果会返回匹配正则表达式的第一个节点的文本(结果不是节点) findall()

find_all,类似于 find,但是 find_all 查询所有符合条件的元素,返回所有匹配的元素组成的列表。

更多

还有诸如find_parents()、find_next_siblings()、find_previous_siblings()等的find,基本使用都差不多,只是搜索范围不同,详见 文档。

CSS选择器

BeautifulSoup 还提供了 CSS 选择器。 使用 CSS 选择器,只需要调用 select() 方法,传入相应的 CSS 选择器即可,返回的结果是符合 CSS 选择器的节点组成的列表:

from bs4 import BeautifulSoup html=''' Hello Foo Bar Jay Foo Bar ''' soup = BeautifulSoup(html, 'lxml') print(soup.select('.panel .panel-heading')) print(soup.select('ul li')) print(soup.select('#list-2 .element')) print(type(soup.select('ul')[0])) 提取信息 获取完整标签

要获取一个标签的完整html代码,只需要写它的节点选择器即可:

soup.title

获取标签类型

利用 name 属性来获取节点的类型(p、a、title、pre 等):

print(soup.title.name)

获取标签内容

正如我们之前所说,调用 string 属性就可以得到节点内的文本:

soup.title.string

⚠️【注意】如果标签下包含其他标签,.string 是不起作用的,它会返回一个 None:

>>> from bs4 import BeautifulSoup >>> html = '

FooBar

' >>> soup = BeautifulSoup(html, 'lxml') >>> print(soup.p.string) None

获取内容,还可以使用节点的 get_text() 方法:

soup.p.get_text()

利用get_text,可以获取标签下所有文本,包括其子节点中的:

>>> from bs4 import BeautifulSoup >>> html = '

FooBar

' >>> soup = BeautifulSoup(html, 'lxml') >>> print(soup.p.string) None >>> print(soup.p.get_text()) FooBar 获取属性

每个节点可能有多个属性,比如 id,class,我们可以调用 attrs 获取所有属性,进而可以通过字典的取值方法(中括号加属性名称,或调用其get()方法)获取特定属性:

print(soup.p.attrs) print(soup.p.attrs['name']) '''(results) {'class': ['title'], 'name': 'dromouse'} dromouse '''

也可以直接使用中括号和属性名:

from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') for ul in soup.select('ul'): print(ul['id']) print(ul.attrs['id']) # 循环体的两行代码等效 第三招 PyQuery

本节主要介绍解析库 PyQuery 的使用。

PyQuery

pyquery: a jquery-like library for python

PyQuery 的使用方法和 jQuery 十分类似。如果您是惯用 jQuery 的老前端了,请不妨一试。

⚠️【注意】需要安装好 PyQuery。

初始化

PyQuery 初始化时可以传入多种形式的数据源,如内容是 HTML 的字符串、源的URL、本地的文件名等。

字符串初始化 from pyquery import PyQuery as pq html = ''' Header

Something

Other thing

In div

''' doc = pq(html) # 传入HTML字符串 print(doc('p')) # 传入CSS选择器 '''(results)

Something

Other thing

In div

''' URL初始化 from pyquery import PyQuery as pq doc = pq(url='http://www.baidu.com', encoding='utf-8') # 这里不写encoding可能中文乱码 print(doc('title')) '''(result) 百度一下,你就知道 ''' CSS选择器

详见 CSS 选择器表。

查找节点 查找子节点用 children('css-selector') 方法,参数为空则为全部。 查找子孙节点用 find('css-selector') 方法,参数不可为空! 查找父节点用 parent('css-selector') 方法,参数为空则为全部。 查找祖先节点用 parents('css-selector') 方法,参数为空则为全部。 查找兄弟节点用 siblings('css-selector') 方法,参数为空则为全部。 >>> p = doc('div') >>> p [, , , , , , , , ] >>> type(p) >>> p.find('#head') [] >>> print(p.find('#head')) ... 遍历

用 PyQuery 选择到的结果可以遍历:

>>> for i in p.parent(): ... print(i, type(i)) ...

注意是lxml的Element了,要用lxml的方法处理。

获取信息 attr() 获取属性 a = doc('a') print(a.attr('href'))

attr() 必须传入要选择的属性名。 若对象包含多个节点,调用对象的attr(),只会返回第一个对象的对应结果。要返回每一个的需要遍历。

text() 获取文本 a = doc('a') a.text()

这个会输出所有包含节点的文本join的结果。

节点操作

PyQuery 还可以操作节点,这个不是本文重点,再次不做介绍。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3